import os
from gurobipy import *
class Data:
    timepiece = 0  # 实际代表时刻 从0时刻开始 相当于区间加1
    OrderNumber = 0 #假设订单0为depot，n+1为depot
    multipleNum = 1  # 对应转换关系 需要手动计算在txt文件中读取
    orderNum = []  # 订单编号
    customerNo = [] #对应的顾客编号
    orderTime = []  # 订单到来时间
    prepareTime = []  # 需要的拣货时间
    orderDemand = []  # 需求量
    serviceTime = []  # 服务时间
    dueTime = []    #最迟时间的软时间窗
    timeMatrix = [[]]  # 记录订单到达时间的区间 定义w(i_s)
    timeNum = []  # 每一时间切片前的订单数量 定义S(t_s)
#客户数据结构
class Customer:
    allNumber = 0   #包含配送中心
    # Cor_X_depot = 0
    # Cor_Y_depot = 0
    allCus = []
    cor_X = []
    cor_Y = []
    distanceMatrix = [[]]
#车辆数据结构
class Vehicle:
    vehicleNum = 0
    vehicleCapacity = 0
def readData(data, path):
    f = open(path, 'r')
    lines = f.readlines()
    data.OrderNumber = len(lines) - 8
    count = 0
    #print(len(lines))
    for line in lines:
        count = count + 1
        if count == 4:
            line = line[:-1].strip()
            str = re.split(r" +", line)
            data.timepiece=int(str[0])
            data.multipleNum = int(str[1])

        if count >= 9 and count < 9 + data.OrderNumber:
            line = line[:-1]
            str = re.split(r" +",line)
            data.orderNum.append(int(str[0]))
            data.customerNo.append(int(str[1]))
            data.orderTime.append(float(str[2]))
            data.prepareTime.append(float(str[3]))
            data.orderDemand.append(float(str[4]))
            data.serviceTime.append(float(str[5]))
            data.dueTime.append(float(str[6]))
    #timepiece s时刻是否能够配送
    data.timeMatrix = [([0] * data.timepiece) for p in range(data.OrderNumber-2)]    #初始化，防止浅拷贝 timepiece列 OrderNumber行
    for i in range(1,data.OrderNumber-1):
        for j in range(0, data.timepiece):
            if (data.orderTime[i] + data.prepareTime[i])/data.multipleNum < j :
                data.timeMatrix[i-1][j] = 1



    data.timeNum = [0] * data.timepiece
    for i in range(1, data.timepiece):
        for k in range(0,data.OrderNumber-2):
            data.timeNum[i] += data.timeMatrix[k][i]

    return data
def readCustomer(customer,path):
    f = open(path,'r')
    lines = f.readlines()
    customer.allNumber = len(lines) - 8
    count = 0
    # print(len(lines))
    for line in lines:
        count = count + 1
        if count == 4:
            line = line[:-1].strip()
            str = re.split(r" +", line)
            customer.allNumber = int(str[0])
            # customer.Cor_X_depot = float(str[1])
            # customer.Cor_Y_depot = float(str[2])
        if count >= 9 and count < 9 + customer.allNumber:
            line = line[:-1]
            str = re.split(r" +", line)
            customer.allCus.append(int(str[0]))
            customer.cor_X.append(float(str[1]))
            customer.cor_Y.append(float(str[2]))
    speed = 1.0
    customer.distanceMatrix = [([0] * customer.allNumber) for p in range(customer.allNumber)]
    for i in range(customer.allNumber):
        for j in range(customer.allNumber):
            temp = (customer.cor_X[i]-customer.cor_X[j]) ** 2 + (customer.cor_Y[i]-customer.cor_Y[j]) ** 2
            customer.distanceMatrix[i][j] = math.sqrt(temp)/speed
            temp = 0
    return customer

data = Data()
path1 = 'order_.txt'
#path1 = 'readdata.txt'
readData(data,path1)

###customer需要改
customer = Customer()
path2 = 'customer_.txt'

readCustomer(customer,path2)

vehicle = Vehicle()
vehicle.vehicleNum = 10
vehicle.vehicleCapacity = 200
BigM = 1e6
I = 20  #最少发车间隔的订单量
lam = 0.03  #发车次数下限比例
ttload = 5
alpha = 1
y = {}#发车时间
z = [([0] * data.timepiece) for p in range(data.OrderNumber)]#发车时间订单对应

model = Model('certain')
#定义决策变量
#y_rs
for i in range(data.timepiece):
    for j in range(data.timepiece):
       if(i != j):
            name = 'y' + str(i) + '_' + str(j)
            y[i,j] = model.addVar(0
                                  ,1
                                  ,vtype = GRB.BINARY
                                  ,name = name)
#z_is
for i in range(data.OrderNumber):
    for j in range(data.timepiece):
        name = 'z' + str(i) + '_' + str(j)
        z[i][j] = model.addVar(0
                                  ,1
                                  ,vtype = GRB.BINARY
                                  ,name = name)

model.update()

#目标函数
obj = LinExpr(0)
for i in range(1,data.OrderNumber-1):
    for j in range(data.timepiece):
        obj.addTerms(alpha * j * data.multipleNum - alpha * data.orderTime[i] - alpha * data.prepareTime[i],z[i][j])



model.setObjective(obj, GRB.MINIMIZE)


#相邻间隔最少订单数
for r in range(data.timepiece-1):
    for s in range(r+1,data.timepiece):
        model.addConstr(data.timeNum[s] - data.timeNum[r] >= I - BigM * (1 - y[r,s]),name='cons0')
#最少趟次约束
lhs = LinExpr(0)
for r in range(data.timepiece-1):
    for s in range(r+1,data.timepiece):
        lhs.addTerms(1 , y[r,s])
model.addConstr(lhs >= data.OrderNumber * lam,name = 'cons2')
#订单i在s时刻发出的必要条件是该时刻拣选完成
for i in range(1,data.OrderNumber-1):
    for j in range(data.timepiece):
        model.addConstr(z[i][j] <= data.timeMatrix[i-1][j],name = str(i)+'_'+str(j)+'biyao')
#订单i需要被配送
for i in range(1,data.OrderNumber-1):
    lhs = LinExpr(0)
    for j in range(data.timepiece):
        lhs.addTerms(1,z[i][j])
    model.addConstr(lhs == 1,name = 'onetime'+'_'+str(i))
#z和y之间的关系1
for r in range(data.timepiece-1):
    for s in range(r+1,data.timepiece):
        lhs = LinExpr(0)
        for i in range(1,data.OrderNumber-1):
            lhs.addTerms(1,z[i][s])
        model.addGenConstrIndicator(y[r,s] , 1 , lhs>=1 , name = 'relationship1'+str(r)+'_'+str(s))

#z和y之间的关系2
for i in range(1,data.OrderNumber-1):
    for s in range(1,data.timepiece):
        lhs = LinExpr(0)
        for r in range(0,s):
            lhs.addTerms(1,y[r,s])
        model.addGenConstrIndicator(z[i][s] , 1 , lhs==1,name = 'relationship2'+str(i)+'_'+str(s))
#起始区间流平衡
lhs = LinExpr(0)
for j in range(1,data.timepiece):
    lhs.addTerms(1,y[0,j])
model.addConstr(lhs == 1, name='flow_conservation_0')
#中间时间段的流平衡

for h in range(1,data.timepiece-1):
    expr1 = LinExpr(0)
    expr2 = LinExpr(0)
    for i in range(h-1):
        expr1.addTerms(1,y[i,h])
    for j in range(h+1,data.timepiece):
        expr2.addTerms(1,y[h,j])
    model.addConstr(expr1==expr2,name='flow_conservation_'+str(h))
    expr1.clear()
    expr2.clear()
#终止区间流平衡
lhs = LinExpr(0)
for j in range(0,data.timepiece-1):
    lhs.addTerms(1,y[j,data.timepiece-1])
model.addConstr(lhs == 1, name='flow_conservation_last')


# 导出模型
model.write('certain.lp')
# 求解
model.optimize()
# 打印结果
print("\n\n-----optimal value-----")
print(model.ObjVal)

for key in y.keys():
    if (y[key].x>0):
        print(y[key].VarName + ' = ', y[key].x)
for i in range(data.OrderNumber):
    for j in range(data.timepiece):
        if(z[i][j].x>0):
            print(z[i][j].VarName + '=' , z[i][j].x)

for j in range(data.timepiece):
    temp1 = 0
    temp = data.multipleNum * j
    for i in range(data.OrderNumber):
        if(z[i][j].x > 0):
            temp1 += 1
            f = open('data_'+str(j)+'.txt', 'a')

            f.write("\n  " +str(temp1)+"  "+str(customer.cor_X[data.customerNo[i]])
                    +"  "+str(customer.cor_Y[data.customerNo[i]])+"  "+str(data.orderDemand[i])+"  "
                    +str(temp)+"  "+str(data.dueTime[i])+"  "+str(data.serviceTime[i])+"  "+ str(data.orderNum[i]))
for i in range(data.timepiece):
    if(os.path.exists('data_'+str(i)+'.txt')):
        f = open('data_' + str(i) + '.txt', 'r+')
        content = f.read()
        f.seek(0,0)
        temp = data.multipleNum * i
        f.write("  0  "+str(customer.cor_X[data.customerNo[0]])
                    +"  "+str(customer.cor_Y[data.customerNo[0]])+"  0  "+str(temp)+"  "
                +str(data.dueTime[0])+"  "+str(data.serviceTime[0])+"  " + str(data.orderNum[0])+content)